python

您所在的位置:网站首页 python socket长连接 python

python

2023-03-12 23:01| 来源: 网络整理| 查看: 265

一说起TCP, 就是什么三次握手, 四次挥手. 而这次想讨论的是:

在不重启各自socket程序情况下, 将ESTABLED链接断开 ??? 情景模拟

简单点, 在同一个机器 通过 nc 来实现 server 和 client 吧

# Server nc -l -p 5555 # Client nc localhost 5555 -p 6666

上面的意思就是, server端在5555端口监听, 而client 通过 6666 端口去连接

为了更加清晰的看到流量, 咱们通过 tcpdump 来观察:

tcpdump -i lo -xnn -S # 因为是本机, 所以lo才能捕获 08:32:01.063394 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [S], seq 1812097880, win 43690, options [mss 65495,sackOK,TS val 2762998 ecr 2761788,nop,wscale 7], length 0 0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 0x0010: 003c 43ae 4000 4006 f90b 7f00 0001 7f00 0x0020: 0001 1a0a 15b3 6c02 6b58 0000 0000 a002 0x0030: aaaa fe30 0000 0204 ffd7 0402 080a 002a 0x0040: 28f6 002a 243c 0103 0307 08:32:01.063416 IP 127.0.0.1.5555 > 127.0.0.1.6666: Flags [S.], seq 1320008227, ack 1812097881, win 43690, options [mss 65495,sackOK,TS val 2762998 ecr 2762998,nop,wscale 7], length 0 0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 0x0010: 003c 0000 4000 4006 3cba 7f00 0001 7f00 0x0020: 0001 15b3 1a0a 4ead ba23 6c02 6b59 a012 0x0030: aaaa fe30 0000 0204 ffd7 0402 080a 002a 0x0040: 28f6 002a 28f6 0103 0307 08:32:01.063431 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [.], ack 1320008228, win 342, options [nop,nop,TS val 2762998 ecr 2762998], length 0 0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 0x0010: 0034 43af 4000 4006 f912 7f00 0001 7f00 0x0020: 0001 1a0a 15b3 6c02 6b59 4ead ba24 8010 0x0030: 0156 fe28 0000 0101 080a 002a 28f6 002a 0x0040: 28f6

而 ss的结果也证明了链接已经建立了:

[[email protected] /]# ss -ant State Recv-Q Send-Q Local Address:Port Peer Address:Port ESTAB 0 0 172.17.0.3:6666 172.17.0.3:5555 ESTAB 0 0 172.17.0.3:5555 172.17.0.3:6666

链接建立之后, 就能互相通信了请在这里输入图片描述

那么如何断开这个链接呢?

错误姿势

现在来试下传统方法, 一般我们会上iptables:

[[email protected] /]# iptables -A INPUT -p tcp --dport 5555 -j DROP [[email protected] /]# iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination DROP tcp -- anywhere anywhere tcp dpt:personal-agent Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination

上面的规则, 意思就是将 目的端口为 5555 的请求丢弃了, 所以我们必须从客户端发信息, 因为客户端才能发到5555端口:请在这里输入图片描述

这里已经看到, 信息已经发不出去了, 而通过tcpdump能看到一堆从client发送的报文:

08:43:44.459584 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [P.], seq 327893533:327893535, ack 3568222208, win 342, options [nop,nop,TS val 2833338 ecr 2832362], length 2 0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 0x0010: 0036 75f8 4000 4006 c6c7 7f00 0001 7f00 0x0020: 0001 1a0a 15b3 138b 421d d4ae c000 8018 0x0030: 0156 fe2a 0000 0101 080a 002b 3bba 002b 0x0040: 37ea 330a 08:43:44.670096 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [P.], seq 327893533:327893535, ack 3568222208, win 342, options [nop,nop,TS val 2833359 ecr 2832362], length 2 0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 0x0010: 0036 75f9 4000 4006 c6c6 7f00 0001 7f00 0x0020: 0001 1a0a 15b3 138b 421d d4ae c000 8018 0x0030: 0156 fe2a 0000 0101 080a 002b 3bcf 002b 0x0040: 37ea 330a 08:43:44.881782 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [P.], seq 327893533:327893535, ack 3568222208, win 342, options [nop,nop,TS val 2833380 ecr 2832362], length 2 0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 0x0010: 0036 75fa 4000 4006 c6c5 7f00 0001 7f00 0x0020: 0001 1a0a 15b3 138b 421d d4ae c000 8018 0x0030: 0156 fe2a 0000 0101 080a 002b 3be4 002b 0x0040: 37ea 330a .... (剩下还有大概 8 条左右)

tcpdump的输出告诉我们client真的已经在努力了, 但是server却不响应, 这真不怪server绝情, 而是它真的没有收到! 都被那可恶的iptables丢掉了.!

那client会因为server不搭理而情绪低落放弃它们的连接么?

[[email protected] /]# ss -ant State Recv-Q Send-Q Local Address:Port Peer Address:Port ESTAB 0 2 127.0.0.1:6666 127.0.0.1:5555 ESTAB 0 0 127.0.0.1:5555 127.0.0.1:6666

很明显, 它们之间是真爱, 尽管server不搭理, client也不会轻易放弃.

而且很有意思的是, tcpdump还在持续的输出:

.....(省略贼多的信息) 08:53:28.844326 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [P.], seq 327893533:327893535, ack 3568222208, win 342, options [nop,nop,TS val 2891776 ecr 2832362], length 2 0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 0x0010: 0036 7606 4000 4006 c6b9 7f00 0001 7f00 0x0020: 0001 1a0a 15b3 138b 421d d4ae c000 8018 0x0030: 0156 fe2a 0000 0101 080a 002c 2000 002b 0x0040: 37ea 330a 08:55:31.721921 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [P.], seq 327893533:327893535, ack 3568222208, win 342, options [nop,nop,TS val 2904064 ecr 2832362], length 2 0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 0x0010: 0036 7607 4000 4006 c6b8 7f00 0001 7f00 0x0020: 0001 1a0a 15b3 138b 421d d4ae c000 8018 0x0030: 0156 fe2a 0000 0101 080a 002c 5000 002b 0x0040: 37ea 330a

比较细心的同学, 可能就会发现, 它们通信的时间, 在不断的增加, 从一开始几毫秒, 到现在的2分钟, 这是由TCP协议中的RTT 和RTO所决定的.

RTT (round trip time)在开启了TCP时间戳后,A记录下时间t1把包发给B,B收到包后记录下时间t2把包回给A ,这个过程里t2-t1就是RTT

RTO(Retransmission TimeOut)即重传超时时间

所以为了节省性能, client重试的时间, 会随着这套算法, 不断的增加~ 但是他们的链接, 已经会长存...至于长存多久, 这个真的取决很多因素, 例如keepalived保活机制等等, 在这里, nc大概13分钟就看不下去了...

那么假设, 还没到那么长时间 而且 iptables良心发现了, 放弃了阻扰, 它们又会怎样呢?

[[email protected] /]# iptables -F [[email protected] /]# iptables -nL Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination

在下次RTO结束时, server就能接收到相应的信息了, 从此client 和 Server又能愉快的玩耍了

花了很大的篇幅来证明client 和 server的真爱, 事实证明它们的专一值得学习!

但是很多时候, 如果client和server冷战, 谁也不理谁, 这就让我们很蛋疼了, 因为如果这样不必要的链接, 长时间保存, 会大量的占用资源, 很快就会出现资源瓶颈, 所以我们一定要扼杀掉这种行为!

正确姿势

首先, 我们得明白的是, 一般的重启程序, 重启机器, 实际上是发送了 fin标识去对端来触发四次挥手发生, 所以对待孽缘, 还是得遵循规律, 从内部攻破..

方法一

在刚才的实验中, iptabls无法阻扰, 但仅仅是因为姿势不对而已, 换个姿势分分钟一刀两段:

[[email protected] /]# iptables -A INPUT -p tcp --dport 5555 -j REJECT --reject-with tcp-reset [[email protected] /]# iptables -nL Chain INPUT (policy ACCEPT) target prot opt source destination REJECT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:5555 reject-with tcp-reset Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination

加了这个, client一发消息就不再是苦苦等待了, 直接就被iptables打耳刮子了

[[email protected] /]# nc localhost 5555 -p 6666 p Ncat: Connection reset by peer.

而ss的结果是:

[[email protected] /]# ss -ant State Recv-Q Send-Q Local Address:Port Peer Address:Port ESTAB 0 0 127.0.0.1:5555 127.0.0.1:6666

tcpdump更是见证了这电光火石的瞬间, 第二个报文的R, 就是 reset的 flags, 这样会client那边的链接直接重置断开.

09:59:55.472340 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [P.], seq 3009865367:3009865369, ack 1955226254, win 342, options [nop,nop,TS val 3290439 ecr 3289331], length 2 0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 0x0010: 0036 d667 4000 4006 6658 7f00 0001 7f00 0x0020: 0001 1a0a 15b3 b366 e697 748a 628e 8018 0x0030: 0156 fe2a 0000 0101 080a 0032 3547 0032 0x0040: 30f3 700a 09:59:55.472362 IP 127.0.0.1.5555 > 127.0.0.1.6666: Flags [R], seq 1955226254, win 0, length 0 0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 0x0010: 0028 0000 4000 4006 3cce 7f00 0001 7f00 0x0020: 0001 15b3 1a0a 748a 628e 0000 0000 5004 0x0030: 0000 fe1c 0000

但是 TCP的全双工的呀, 刚才断的只是, client 到 server的, 还有server 到client的, 按照刚才的方法, 反过来搞一发!

PS: 要注意先把刚才的 INPUT 的删掉, 要不然reset就会被自己禁掉, 无法发送给 5555了..

[email protected] /]# iptables -F [[email protected] /]# iptables -A OUTPUT -p tcp --dport 6666 -j REJECT --reject-with tcp-reset [[email protected] /]# iptables -nL Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination REJECT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:6666 reject-with tcp-reset

等到server一发送消息, 也马上挂了

[[email protected] /]# nc -l -p 5555 p [[email protected] /]#

ss的结果:

[[email protected] /]# ss -nat State Recv-Q Send-Q Local Address:Port Peer Address:Port

而一边吃瓜看戏的tcpdump..:

14:54:59.045584 IP 127.0.0.1.6666 > 127.0.0.1.5555: Flags [R], seq 379940499, win 0, length 0 0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 0x0010: 0028 0000 4000 4006 3cce 7f00 0001 7f00 0x0020: 0001 1a0a 15b3 16a5 6e93 0000 0000 5004 0x0030: 0000 fe1c 0000 # 这里可能会有点疑问, 为什么从server 5555 发出的报文会没看到, 我猜测是因为这个报文还没到tcpdump就已经被iptables处理并直接返回了..

于是这对冤家就这样各奔东西, 相忘于江湖.

方法二

虽然这个方法比较好使, 但是操作起来真的挺恶心..而且还挺容易误伤, 所以有第二种方法, 简单又优雅: tcpkill直接:

tcpkill -1 -i eth0 port 5555

等到它们互相发送消息, 就能直接干掉了..请在这里输入图片描述

tcpkill的原理和刚才的iptables相似, 也是发送了一个链接重置的R标志报文, 迫使对方关闭断开连接, 只是相对而言会比较智能一点, 因为它会自动构造报文并发送.详情可以看: https://github.com/stanzgy/wi...

总结

其实到这里, 大家应该有些印象, 不管是第一种方法, 还是第二种方法, 都离不开那个神奇的R, 但这些又是什么?

这些就是在TCP通信过程中, 起着决定性的作用标志位flags, 主要有下面几个:

SYN: 表示建立连接, FIN: 表示关闭连接, ACK: 表示响应, PSH: 表示有 DATA数据传输, RST: 表示连接重置。

上面的方法所用到就是最后一种标志:RST重置链接

所以总得而言, iptables的DROP行为, 能够阻止链接的建立, 但是对于已经建立起来的链接, 顶多只能阻止数据的传输, 但是不能断开链接, 链接的断开应该只有下面几种可能:

socket 的主动close, 也就是发送 fin报文 ( 应用层程序或者内核 ) TCP链接的超时自动断开 ( 这个过程可能会比较耗时 ) 伪造报文发送RST

除了上面的条件, 还有一个点需要注意的, 那就是:

在某些情况下, 哪怕对方关闭了, 但是自己也是无法感知的, 还是需要send一次, 通信一次, 触发了socket的错误, 例如 Connection reset by peer. 或者 Broken pipe等等, 才能知道自己可以关闭, 否则大家都不通信, 这样有心无力, 只能听天由命了!

欢迎各位大神指点交流, QQ讨论群: 258498217, 转载请注明来源: https://segmentfault.com/a/11...



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3